{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Агент «Продавец телефонов»\n",
    "\n",
    "Раздел содержит пример разработки агента, который использует модель GigaChat, чтобы:\n",
    "\n",
    "* Рассказывать о доступных моделях телефонов на основе заданной базы данных.\n",
    "* Детально описывать характеристики выбранной модели телефона.\n",
    "* Оформлять заказы на покупку телефонов.\n",
    "\n",
    "## Добавление базы данных и инструментов\n",
    "\n",
    "Чтобы агент мог рассказать пользователю о доступных телефонах, создайте массив с описанием каждой модели:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "stuff_database = [\n",
    "    {\n",
    "        \"name\": \"iPhone 8 mini\",\n",
    "        \"price\": 300,\n",
    "        \"memory\": 128,\n",
    "        \"ram\": 8,\n",
    "        \"camera\": 12,\n",
    "        \"description\": \"Самая дешевая модель iPhone\",\n",
    "    },\n",
    "    {\n",
    "        \"name\": \"iPhone 14\",\n",
    "        \"price\": 1000,\n",
    "        \"memory\": 128,\n",
    "        \"ram\": 8,\n",
    "        \"camera\": 12,\n",
    "        \"description\": \"Телефон будущего, уже сегодня!\",\n",
    "    },\n",
    "    {\n",
    "        \"name\": \"Samsung Galaxy S23\",\n",
    "        \"price\": 900,\n",
    "        \"memory\": 256,\n",
    "        \"ram\": 12,\n",
    "        \"camera\": 108,\n",
    "        \"description\": \"Камера такая острая, что сможет увидеть даже ваши ошибки\",\n",
    "    },\n",
    "    {\n",
    "        \"name\": \"Google Pixel 7\",\n",
    "        \"price\": 850,\n",
    "        \"memory\": 128,\n",
    "        \"ram\": 8,\n",
    "        \"camera\": 16,\n",
    "        \"description\": \"Для тех, кто хочет получить стоковый Android и хорошие фотки\",\n",
    "    },\n",
    "    {\n",
    "        \"name\": \"OnePlus 9T\",\n",
    "        \"price\": 700,\n",
    "        \"memory\": 128,\n",
    "        \"ram\": 8,\n",
    "        \"camera\": 48,\n",
    "        \"description\": \"Зарядка быстрее, чем ваш кофе\",\n",
    "    },\n",
    "    {\n",
    "        \"name\": \"Xiaomi Mi 12\",\n",
    "        \"price\": 600,\n",
    "        \"memory\": 128,\n",
    "        \"ram\": 6,\n",
    "        \"camera\": 64,\n",
    "        \"description\": \"Бюджетный флагман для ценителей вкуса\",\n",
    "    },\n",
    "    {\n",
    "        \"name\": \"Sony Xperia 3\",\n",
    "        \"price\": 1100,\n",
    "        \"memory\": 256,\n",
    "        \"ram\": 12,\n",
    "        \"camera\": 20,\n",
    "        \"description\": \"Телефон для тех, кто скучал по кнопке для камеры\",\n",
    "    },\n",
    "    {\n",
    "        \"name\": \"Huawei P60\",\n",
    "        \"price\": 800,\n",
    "        \"memory\": 128,\n",
    "        \"ram\": 8,\n",
    "        \"camera\": 50,\n",
    "        \"description\": \"Для любителей ночной съемки и без Google Play\",\n",
    "    },\n",
    "    {\n",
    "        \"name\": \"Nokia 10 PureView\",\n",
    "        \"price\": 750,\n",
    "        \"memory\": 128,\n",
    "        \"ram\": 6,\n",
    "        \"camera\": 48,\n",
    "        \"description\": \"Nokia вернулась, и у неё есть змейка!\",\n",
    "    },\n",
    "    {\n",
    "        \"name\": \"LG Velvet 2\",\n",
    "        \"price\": 650,\n",
    "        \"memory\": 128,\n",
    "        \"ram\": 8,\n",
    "        \"camera\": 32,\n",
    "        \"description\": \"Потому что жизнь хороша\",\n",
    "    },\n",
    "    {\n",
    "        \"name\": \"Asus ROG Phone 6\",\n",
    "        \"price\": 1000,\n",
    "        \"memory\": 512,\n",
    "        \"ram\": 16,\n",
    "        \"camera\": 64,\n",
    "        \"description\": \"Играй как профи, заряжай как новичок\",\n",
    "    },\n",
    "    {\n",
    "        \"name\": \"Motorola Edge Plus\",\n",
    "        \"price\": 700,\n",
    "        \"memory\": 128,\n",
    "        \"ram\": 8,\n",
    "        \"camera\": 108,\n",
    "        \"description\": \"Край к краю, пиксель к пикселю\",\n",
    "    },\n",
    "    {\n",
    "        \"name\": \"Realme X4 Pro\",\n",
    "        \"price\": 450,\n",
    "        \"memory\": 128,\n",
    "        \"ram\": 8,\n",
    "        \"camera\": 48,\n",
    "        \"description\": \"Экономия без потерь в качестве\",\n",
    "    },\n",
    "    {\n",
    "        \"name\": \"Oppo Find X4\",\n",
    "        \"price\": 900,\n",
    "        \"memory\": 256,\n",
    "        \"ram\": 12,\n",
    "        \"camera\": 50,\n",
    "        \"description\": \"Найди X, но без математики\",\n",
    "    },\n",
    "    {\n",
    "        \"name\": \"BlackBerry Secure\",\n",
    "        \"price\": 1200,\n",
    "        \"memory\": 128,\n",
    "        \"ram\": 8,\n",
    "        \"camera\": 12,\n",
    "        \"description\": \"Для тех, кто ещё помнит, что такое физическая клавиатура\",\n",
    "    },\n",
    "    {\n",
    "        \"name\": \"Fairphone 4\",\n",
    "        \"price\": 500,\n",
    "        \"memory\": 64,\n",
    "        \"ram\": 4,\n",
    "        \"camera\": 12,\n",
    "        \"description\": \"Этичный выбор для заботливого потребителя\",\n",
    "    },\n",
    "]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Добавьте функции, которые агент будет вызывать для работы с базой данных.\n",
    "\n",
    "Чтобы упросить создание функций из python-кода используйте декоратор `@tool`.\n",
    "Он преобразует любую функцию в инструмент, доступный модели для вызова.\n",
    "\n",
    ":::note\n",
    "\n",
    "Модель ориентируется как на название и описание функции, так и на описание и типы аргументов возвращаемого значения.\n",
    "Чтобы модель правильно понимала как нужно использовать инструмент, все значения функции нужно явно указать.\n",
    "\n",
    ":::\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import Dict\n",
    "\n",
    "from langchain.tools import tool\n",
    "\n",
    "\n",
    "@tool\n",
    "def get_all_phone_names() -> str:\n",
    "    \"\"\"Возвращает названия моделей всех телефонов через запятую\"\"\"\n",
    "    # Подсвечивает вызов функции зеленым цветом\n",
    "    print(\"\\033[92m\" + \"Bot requested get_all_phone_names()\" + \"\\033[0m\")\n",
    "    return \", \".join([stuff[\"name\"] for stuff in stuff_database])\n",
    "\n",
    "\n",
    "@tool\n",
    "def get_phone_data_by_name(name: str) -> Dict:\n",
    "    \"\"\"\n",
    "    Возвращает цену, характеристики и описание телефона по точному названию модели.\n",
    "\n",
    "    Args:\n",
    "        name (str): Точное название модели телефона.\n",
    "\n",
    "    Returns:\n",
    "        Dict: Словарь с информацией о телефоне (цена, характеристики и описание).\n",
    "    \"\"\"\n",
    "    # Подсвечивает вызов функции зеленым цветом\n",
    "    print(\"\\033[92m\" + f\"Bot requested get_phone_data_by_name({name})\" + \"\\033[0m\")\n",
    "    for stuff in stuff_database:\n",
    "        if stuff[\"name\"] == name:\n",
    "            return stuff\n",
    "\n",
    "    return {}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Добавьте функцию, с помощью которой агент будет создавать заказы:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "@tool\n",
    "def create_order(name: str, phone: str) -> None:\n",
    "    \"\"\"\n",
    "    Создает новый заказ на телефон.\n",
    "\n",
    "    Args:\n",
    "        name (str): Название телефона.\n",
    "        phone (str): Телефонный номер пользователя.\n",
    "\n",
    "    Returns:\n",
    "        str: Статус заказа.\n",
    "    \"\"\"\n",
    "    # Подсвечивает вызов функции зеленым цветом\n",
    "    print(\"\\033[92m\" + f\"Bot requested create_order({name}, {phone})\" + \"\\033[0m\")\n",
    "    print(f\"!!! NEW ORDER !!! {name} {phone}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "С помощью системного промпта опишите роль, которой должна следовать модель:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "system_prompt = \"Ты бот-продавец телефонов. Твоя задача продать телефон пользователю, получив от него заказ. Если тебе не хватает каких-то данных, запрашивай их у пользователя.\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Чтобы дать модели доступ ко всем инструментам, перечислите их в массиве:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "tools = [get_all_phone_names, get_phone_data_by_name, create_order]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Создание агента\n",
    "\n",
    "Перед созданием агента инициализируйте GigaChat и укажите модель, поддерживающую работу с функциями:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.chat_models.gigachat import GigaChat\n",
    "\n",
    "giga = GigaChat(\n",
    "    credentials=\"<авторизационные_данные>\", model=\"<модель_с_поддержкой_функций>\"\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Для авторизации запросов к GigaChat используйте данные, полученные при [создании проекта GigaChat API](https://developers.sber.ru/docs/ru/gigachat/api/integration).\n",
    "\n",
    "Список и описание доступных моделей ищите в разделе [Модели GigaChat](https://developers.sber.ru/docs/ru/gigachat/models).\n",
    "\n",
    "Инициализируйте агента и передайте ему экземпляр класса GigaChat, и массив доступных инструментов `tools`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.agents import AgentExecutor, create_gigachat_functions_agent\n",
    "\n",
    "agent = create_gigachat_functions_agent(giga, tools)\n",
    "\n",
    "# AgentExecutor создает среду, в которой будет работать агент\n",
    "agent_executor = AgentExecutor(\n",
    "    agent=agent,\n",
    "    tools=tools,\n",
    "    verbose=False,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Сохранение контекста диалога\n",
    "\n",
    "Чтобы агент помнил о чем пользователь разговаривал с ним, передайте историю сообщений в переменной `chat_history`.\n",
    "\n",
    "Вы можете использовать память для сохранения истории и промежуточных результатов общения пользователя с агентом:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "User: Какие у вас есть модели айфонов?\n",
      "\u001b[92mBot requested get_all_phone_names()\u001b[0m\n",
      "Bot: У нас есть следующие модели айфонов: iPhone 8 mini, iPhone 14. Какие-то из них Вас интересуют?\n",
      "User: Сколько стоит 14й?\n",
      "\u001b[92mBot requested get_phone_data_by_name(iPhone 14)\u001b[0m\n",
      "Bot: iPhone 14 стоит 1000 долларов. Он имеет 128 ГБ памяти и 8 ГБ оперативной памяти. Основная камера имеет разрешение 12 мегапикселей. Описание: \"Телефон будущего, уже сегодня!\" Хотели бы Вы приобрести этот телефон?\n",
      "User: Дорого. Тогда я хочу купить 8 mini\n",
      "\u001b[92mBot requested get_phone_data_by_name(iPhone 8 mini)\u001b[0m\n",
      "Bot: iPhone 8 mini стоит 300 долларов. Он имеет 128 ГБ памяти и 8 ГБ оперативной памяти. Основная камера имеет разрешение 12 мегапикселей. Описание: \"Самая дешевая модель iPhone\". Хотели бы Вы приобрести этот телефон?\n",
      "User: Да, оформи заказ\n",
      "\u001b[92mBot requested get_phone_data_by_name(iPhone 8 mini)\u001b[0m\n",
      "Bot: Отлично! Для завершения заказа мне нужен Ваш номер телефона.\n",
      "User: +79222232233\n",
      "\u001b[92mBot requested create_order(iPhone 8 mini, +79222232233)\u001b[0m\n",
      "!!! NEW ORDER !!! iPhone 8 mini +79222232233\n",
      "Bot: Ваш заказ на iPhone 8 mini успешно оформлен. Ваш номер телефона для связи: +79222232233.\n",
      "User: Спасибо!\n",
      "Bot: Пожалуйста! Если у Вас есть ещё вопросы, задавайте.\n",
      "User: \n"
     ]
    }
   ],
   "source": [
    "from langchain_core.messages import AIMessage, HumanMessage, SystemMessage\n",
    "\n",
    "chat_history = [SystemMessage(content=system_prompt)]\n",
    "while True:\n",
    "    user_input = input(\"Покупатель: \")\n",
    "    print(f\"User: {user_input}\")\n",
    "    if user_input == \"\":\n",
    "        break\n",
    "    result = agent_executor.invoke(\n",
    "        {\n",
    "            \"chat_history\": chat_history,\n",
    "            \"input\": user_input,\n",
    "        }\n",
    "    )\n",
    "    chat_history.append(HumanMessage(content=user_input))\n",
    "    chat_history.append(AIMessage(content=result[\"output\"]))\n",
    "    print(f\"Bot: {result['output']}\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
